package com.smartleanx.module.hrm.service.risk;

import cn.hutool.core.collection.CollUtil;
import com.smartleanx.framework.common.pojo.CommonResult;
import com.smartleanx.framework.common.util.collection.CollectionUtils;
import com.smartleanx.module.hrm.convert.risk.RiskConvert;
import com.smartleanx.module.hrm.dal.dataobject.companysystem.CompanySystemDO;
import com.smartleanx.module.hrm.dal.dataobject.companysystemtype.CompanySystemTypeDO;
import com.smartleanx.module.hrm.dal.dataobject.employee.EmployeeDO;
import com.smartleanx.module.hrm.dal.dataobject.risk.RiskDO;
import com.smartleanx.module.hrm.dal.mysql.risk.RiskMapper;
import com.smartleanx.module.hrm.eums.employee.EmployeeStatusEnum;
import com.smartleanx.module.hrm.eums.risk.RiskLevelEnum;
import com.smartleanx.module.hrm.eums.risk.RiskTypeEnum;
import com.smartleanx.module.hrm.intergration.DeptServiceProvider;
import com.smartleanx.module.hrm.intergration.FileServiceProvider;
import com.smartleanx.module.hrm.service.companysystem.CompanySystemService;
import com.smartleanx.module.hrm.service.companysystemtype.CompanySystemTypeService;
import com.smartleanx.module.hrm.service.employee.EmployeeService;
import com.smartleanx.module.infra.api.file.dto.FileRefQueryDTO;
import com.smartleanx.module.infra.api.file.dto.FileRefRespDTO;
import com.smartleanx.module.system.api.dept.PostApi;
import com.smartleanx.module.system.api.dept.dto.DeptRespDTO;
import com.smartleanx.module.system.api.dept.dto.PostRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.smartleanx.module.hrm.controller.admin.risk.vo.*;
import com.smartleanx.module.hrm.dal.dataobject.risk.RiskTypeDO;

import com.smartleanx.module.hrm.dal.mysql.risk.RiskTypeMapper;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.smartleanx.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.smartleanx.module.hrm.enums.ErrorCodeConstants.*;

/**
 * 风险点 Service 实现类
 *
 * @author tengcj
 */
@Service
@Validated
public class RiskServiceImpl implements RiskService {

    @Resource
    private RiskTypeMapper riskTypeMapper;

    @Resource
    private RiskMapper riskMapper;

    @Resource
    private FileServiceProvider fileServiceProvider;

    @Resource
    @Lazy
    private EmployeeService employeeService;

    @Resource
    private DeptServiceProvider deptServiceProvider;

    @Resource
    private CompanySystemService companySystemService;

    @Resource
    private CompanySystemTypeService companySystemTypeService;

    @Autowired
    private PostApi postApi;

    @Override
    public RiskProfileRespVO getProfile() {
        RiskProfileRespVO respVO = new RiskProfileRespVO();

        Long majorNum = riskMapper.selectCountByRiskLevel(RiskLevelEnum.MAJOR.getLevel());
        Long normalNum = riskMapper.selectCountByRiskLevel(RiskLevelEnum.NORMAL.getLevel());
        respVO.setMajorRiskNum(majorNum.intValue());
        respVO.setNormalRiskNum(normalNum.intValue());
        respVO.setRiskNum(respVO.getNormalRiskNum() + respVO.getMajorRiskNum());

        List<RiskDO> riskDOList = riskMapper.selectEmpRiskList(null, null, null, null);
        Set<Long> empIds = riskDOList.stream().map(RiskDO::getEmployeeId).filter(Objects::nonNull).collect(Collectors.toSet());
        respVO.setRiskEmpNum(empIds.size());

        return respVO;
    }

    @Override
    public RiskDetailRespVO getDetail(Integer riskLevel) {
        RiskDetailRespVO detailRespVO = new RiskDetailRespVO();

        List<RiskTypeDO> allRisk = getAllRisk();
        List<RiskDO> riskList = riskMapper.selectEmpRiskList(null, null,null, riskLevel);

        List<RiskEmployeeRespVO> riskEmployeeRespVOList = buildRiskEmpResVOList(riskList);

        Map<Long, List<RiskDO>> riskMap = CollectionUtils.convertMultiMap(riskList, RiskDO::getRiskId);

        if (riskLevel != null) {
            allRisk = allRisk.stream().filter(riskType -> riskType.getLevel().equals(riskLevel)).collect(Collectors.toList());
            Map<Long, RiskTypeDO> riskTypeMap = CollectionUtils.convertMap(allRisk, RiskTypeDO::getId);
            for (Long riskId : riskMap.keySet()) {
                if (!riskTypeMap.containsKey(riskId)) {
                    allRisk.add(riskTypeMapper.selectById(riskId));
                }
            }
        }

        List<RiskRespVO> riskRespVOList = RiskConvert.INSTANCE.convertRespVOList(allRisk);

        riskRespVOList.forEach(riskRespVO -> {
            List<RiskDO> riskDOList = riskMap.get(riskRespVO.getId());
            if (CollUtil.isNotEmpty(riskDOList)) {
                riskRespVO.setRiskEmpNum(riskDOList.size());
            } else {
                riskRespVO.setRiskEmpNum(0);
            }
        });

        Map<String, Integer> collect = riskRespVOList.stream()
                .collect(Collectors.groupingBy(RiskRespVO::getModule, Collectors.summingInt(RiskRespVO::getRiskEmpNum)));

        riskRespVOList.forEach(riskRespVO -> {
            // 模块数量
            riskRespVO.setModuleRiskEmpNum(collect.get(riskRespVO.getModule()) == null ? 0 : collect.get(riskRespVO.getModule()));
        });

        detailRespVO.setRiskList(riskRespVOList);

        detailRespVO.setRiskEmpList(riskEmployeeRespVOList);

        return detailRespVO;
    }

    @Override
    public List<RiskEmployeeRespVO> getRiskEmployee(RiskQueryReqVO queryReqVO) {
        return buildRiskEmpResVOList(
                riskMapper.selectEmpRiskList(queryReqVO.getEmployeeId(), queryReqVO.getDeptId(), queryReqVO.getRiskId(), null)
        );
    }

    public List<RiskEmployeeRespVO> buildRiskEmpResVOList(List<RiskDO> riskList) {
        if (CollUtil.isEmpty(riskList)) {
            return Collections.emptyList();
        }
        Map<Long, RiskTypeEnum> riskMap = Arrays.stream(RiskTypeEnum.values()).collect(Collectors.toMap(RiskTypeEnum::getCode, Function.identity()));

        riskList = riskList.stream().filter(riskDO -> riskDO.getEmployeeId() != null).collect(Collectors.toList());

        Map<Long, List<RiskDO>> empRiskMap = CollectionUtils.convertMultiMap(riskList, RiskDO::getEmployeeId);

        Map<Long, RiskTypeDO> riskTypeMap = CollectionUtils.convertMap(getAllRisk(), RiskTypeDO::getId);

//        List<EmployeeDO> employeeList = employeeService.getEmployeeList(empRiskMap.keySet());
        List<EmployeeDO> employeeList = employeeService.getEmployeeListDesc(empRiskMap.keySet());

        Set<Long> recordIds = riskList.stream().filter(
                riskDO -> Objects.equals(riskDO.getRiskId(), RiskTypeEnum.REGULATION_NOT_INFORM.getCode()) && riskDO.getRecordId() != null
        ).map(RiskDO::getRecordId).collect(Collectors.toSet());

        Map<Long, CompanySystemDO> companySystemMap = CollectionUtils.convertMap(
                companySystemService.getCompanySystemList(recordIds, null), CompanySystemDO::getId
        );

        Set<Long> deptIdList = employeeList.stream().map(EmployeeDO::getDeptId).collect(Collectors.toSet());
        Map<Long, DeptRespDTO> deptMap = deptServiceProvider.getDeptMap(deptIdList);

        List<RiskEmployeeRespVO> riskEmployeeList = new ArrayList<>();

        employeeList.forEach(employeeDO -> {
            RiskEmployeeRespVO riskEmployeeRespVO = RiskConvert.INSTANCE.convertRiskEmpRespVO(employeeDO);
            DeptRespDTO deptRespDTO = deptMap.get(employeeDO.getDeptId());
            if (deptRespDTO != null) {
                riskEmployeeRespVO.setDeptName(deptRespDTO.getName());
            }
            CommonResult<PostRespDTO> post = postApi.getPost(employeeDO.getPostId());
            if(post.getData()!=null){
                riskEmployeeRespVO.setPostName(post.getData().getName());
            }

            List<RiskDO> empRiskList = empRiskMap.get(employeeDO.getId());
            if (CollUtil.isEmpty(empRiskList)) {
                return;
            }
            empRiskList.forEach(riskDO -> {
                RiskRespVO riskRespVO = RiskConvert.INSTANCE.convertRespVO(riskTypeMap.get(riskDO.getRiskId()));
                if (riskRespVO == null) {
                    return;
                }
                if (riskRespVO.getId().equals(RiskTypeEnum.REGULATION_NOT_INFORM.getCode())) {
                    CompanySystemDO companySystemDO = companySystemMap.get(riskDO.getRecordId());
                    if (companySystemDO != null) {
                        riskRespVO.setContent(
                                String.format(RiskTypeEnum.REGULATION_NOT_INFORM.getContent(), companySystemDO.getName())
                        );
                    }
                }
                riskRespVO.setLevel(riskDO.getLevel());
                RiskTypeEnum riskTypeEnum = riskMap.get(riskDO.getRiskId());
                riskRespVO.setRedirectType(riskTypeEnum.getRedirectType());
                riskRespVO.setStage(riskTypeEnum.getStage());
                riskRespVO.setStageName(riskTypeEnum.getStageName());

                if(riskTypeEnum.getBizType() != null) {
                    FileRefQueryDTO refQueryDTO = new FileRefQueryDTO();
                    refQueryDTO.setBizType(riskTypeEnum.getBizType());
                    refQueryDTO.setBizFileTypes(Collections.singletonList(riskTypeEnum.getBizFileType()));
                    refQueryDTO.setBizRecordIds(Collections.singletonList(employeeDO.getId()));
                    List<FileRefRespDTO> fileRef = fileServiceProvider.getFileRef(refQueryDTO);
                    riskRespVO.setRefRespDTOList(fileRef);
                }

                riskRespVO.setBizType(riskTypeEnum.getBizType());
                riskRespVO.setBizFileType(riskTypeEnum.getBizFileType());
                riskRespVO.setName(riskTypeEnum.getName());
                riskRespVO.setEmploymentForm(employeeDO.getEmploymentForm());
                riskEmployeeRespVO.getRiskList().add(riskRespVO);
            });
            riskEmployeeList.add(riskEmployeeRespVO);
        });
        return riskEmployeeList;
    }


    private List<RiskTypeDO> getAllRisk() {
        return riskTypeMapper.selectList();
    }

    @Override
    public RiskTypeDO getRiskType(Long id) {
        return riskTypeMapper.selectById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addRiskToEmployee(Long employeeId, Long riskType, Integer level) {
        // RiskDO empRisk = riskMapper.selectEmpRisk(employeeId, riskType, null);
        // Long deptId = null;
        // if (employeeId != null) {
        //     EmployeeDO employee = employeeService.getEmployeeById(employeeId);
        //     if (employee == null) {
        //         return;
        //     }
        //     deptId = employee.getDeptId();
        // }
        // if (empRisk == null) {
        //     RiskDO riskDO = new RiskDO();
        //     riskDO.setEmployeeId(employeeId);
        //     riskDO.setRiskId(riskType);
        //     riskDO.setLevel(level);
        //     riskDO.setDeptId(deptId);
        //     riskMapper.insert(riskDO);
        //     return;
        // }
        // if (!Objects.equals(empRisk.getDeptId(), deptId)
        //         || (!Objects.equals(empRisk.getLevel(), level) && !empRisk.getIgnored())) {
        //     RiskDO update = new RiskDO();
        //     update.setId(empRisk.getId());
        //     if (!Objects.equals(empRisk.getDeptId(), deptId)) {
        //         update.setDeptId(deptId);
        //     }
        //     if (!Objects.equals(empRisk.getLevel(), level) && !empRisk.getIgnored()) {
        //         update.setLevel(level);
        //     }
        //     riskMapper.updateById(update);
        // }
        addRisk(employeeId, riskType, level, null);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addRisk(Long employeeId, Long riskType, Integer level, Long recordId) {
        RiskDO empRisk = riskMapper.selectEmpRisk(employeeId, riskType, recordId);
        Long deptId = null;
        if (employeeId != null) {
            EmployeeDO employee = employeeService.getEmployeeById(employeeId);
            if (employee == null) {
                return;
            }
            deptId = employee.getDeptId();
        }
        if (empRisk == null) {
            RiskDO riskDO = new RiskDO();
            riskDO.setEmployeeId(employeeId);
            riskDO.setRiskId(riskType);
            riskDO.setLevel(level);
            riskDO.setRecordId(recordId);
            riskDO.setDeptId(deptId);
            riskMapper.insert(riskDO);
            return;
        }

        if (empRisk.getIgnored()) {
            return;
        }

        if (!Objects.equals(empRisk.getLevel(), level)) {
            RiskDO update = new RiskDO();
            update.setId(empRisk.getId());
            update.setLevel(level);
            riskMapper.updateById(update);
        }
    }

    @Override
    public void deleteRisk(Long employeeId, Long riskType, Long recordId) {
        RiskDO empRisk = riskMapper.selectEmpRisk(employeeId, riskType, recordId);
        if (empRisk != null) {
            riskMapper.deleteById(empRisk);
        }
    }

    @Override
    public void deleteRisk(Long employeeId, Long riskType) {
        List<RiskDO> riskDOList = riskMapper.selectEmpRisk(CollUtil.newHashSet(employeeId), riskType);
        if (CollUtil.isNotEmpty(riskDOList)) {
            riskMapper.deleteBatchIds(riskDOList);
        }
    }

    @Override
    public void deleteRisk(Set<Long> employeeIds, Long riskType) {
        List<RiskDO> empRiskList = riskMapper.selectEmpRisk(employeeIds, riskType);
        if (CollUtil.isNotEmpty(empRiskList)) {
            riskMapper.deleteBatchIds(empRiskList);
        }
    }

    @Override
    public void deleteRisk(Long riskType, Set<Long> recordIds) {
        List<RiskDO> riskDOList = riskMapper.selectRisk(riskType, recordIds);
        if (CollUtil.isNotEmpty(riskDOList)) {
            riskMapper.deleteBatchIds(riskDOList);
        }
    }

    @Override
    public void cancelRiskEmp(RiskCancelRiskEmpReqVO reqVO) {
        if (reqVO.getRiskId() != null && riskTypeMapper.selectById(reqVO.getRiskId()) == null) {
            throw exception(RISK_TYPE_NOT_EXISTS);
        }
        riskMapper.cancelRisk(reqVO);
    }

    @Override
    public void deleteRiskByEmployeeId(Long employeeId) {
        riskMapper.deleteRiskByEmpId(employeeId);
    }

    @Override
    public void cancelEmpRisk(RiskCancelEmpRiskReqVO reqVO) {
        List<RiskDO> riskDOList = riskMapper.selectEmpRiskList(reqVO.getEmployeeId(), null, null, null);
        if (CollUtil.isEmpty(riskDOList)) {
            throw exception(RISK_EMPLOYEE_NOT_EXISTS);
        }
        riskMapper.cancelRisk(reqVO);
    }

    @Override
    public List<RiskRegulationRespVO> getRiskRegulation(Long riskId) {
        if (!Objects.equals(riskId, RiskTypeEnum.REGULATION_UNDEMOCRATIC.getCode())) {
            return Collections.emptyList();
        }
        List<RiskDO> riskList = riskMapper.selectEmpRiskList(null, null, riskId, null);

        Set<Long> idList = riskList.stream().map(RiskDO::getRecordId).filter(Objects::nonNull).collect(Collectors.toSet());

        List<CompanySystemDO> companySystemList = companySystemService.getCompanySystemList(idList, null);

        Map<Long, CompanySystemTypeDO> typeMap = CollectionUtils.convertMap(
                companySystemTypeService.getTypeList(
                        CollectionUtils.convertSet(companySystemList, CompanySystemDO::getTypeId)
                ), CompanySystemTypeDO::getId
        );

        List<RiskRegulationRespVO> result = RiskConvert.INSTANCE.convertRiskRegulationRespVOList(companySystemList);

        for (RiskRegulationRespVO respVO : result) {
            if (respVO.getTypeId() != null) {
                CompanySystemTypeDO typeDO = typeMap.get(respVO.getTypeId());
                if (typeDO != null) {
                    respVO.setTypeName(typeDO.getName());
                }
            }
        }

        return result;
    }

    @Override
    public void changeEmpRiskDept(Long employeeId, Long deptId) {
        if (employeeId == null) {
            return;
        }
        List<RiskDO> riskDOList = riskMapper.selectEmpRisk(Collections.singleton(employeeId), null);
        if (CollUtil.isNotEmpty(riskDOList)) {
            riskMapper.updateEmpDept(employeeId, deptId);
        }
    }
}
